﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using ZyGame.Execute;

namespace ZyGame
{
    public static class GameObjectExtension
    {
        public static GameObject Instantiate(this GameObject gameObject, GameObject parent)
        {
            GameObject clone = GameObject.Instantiate(gameObject);
            clone.SetParent(parent, Vector3.zero, Vector3.zero, Vector3.one);
            return clone;
        }

        public static GameObject Instantiate(this GameObject gameObject, GameObject parent, Vector3 position)
        {
            GameObject clone = GameObject.Instantiate(gameObject);
            clone.SetParent(parent, position, Vector3.zero, Vector3.one);
            return clone;
        }

        public static GameObject Instantiate(this GameObject gameObject, GameObject parent, Vector3 position, Vector3 rotation)
        {
            GameObject clone = GameObject.Instantiate(gameObject);
            clone.SetParent(parent, position, rotation, Vector3.one);
            return clone;
        }

        public static GameObject Instantiate(this GameObject gameObject, GameObject parent, Vector3 position, Vector3 rotation, Vector3 scale)
        {
            GameObject clone = GameObject.Instantiate(gameObject);
            clone.SetParent(parent, position, rotation, scale);
            return clone;
        }

        public static void ListenerUnityEvent<T>(this GameObject gameObject, UnityEventNames eventNames, Action<T> action) where T : class
        {
            if (gameObject == null)
            {
                return;
            }

            Client.Tools.UnityEventListener(gameObject, eventNames, args => action(args as T));
        }

        public static void ListenerUnityEvent(this GameObject gameObject, UnityEventNames eventNames, Action<object> action)
        {
            if (gameObject == null)
            {
                return;
            }

            gameObject.ListenerUnityEvent<object>(eventNames, action);
        }

        //public static void DOScale(this GameObject gameObject,Vector3 target,float time,int ease,)
        public static string GetInEditorAssetPath(this GameObject gameObject)
        {
#if UNITY_EDITOR
            return UnityEditor.AssetDatabase.GetAssetPath(gameObject);
#else
            return string.Empty;
#endif
        }

        public static string SavePrefab(this GameObject gameObject)
        {
#if UNITY_EDITOR
            GameObject temp = GameObject.Instantiate<GameObject>(gameObject);
            string path = Path.GetDirectoryName(gameObject.GetInEditorAssetPath()) + "/" + Path.GetFileNameWithoutExtension(gameObject.name) + ".prefab";
            GameObject asset = UnityEditor.PrefabUtility.SaveAsPrefabAsset(temp, path);
            GameObject.DestroyImmediate(temp);
            return GetInEditorAssetPath(asset);
#else
            return string.Empty;
#endif
        }

        // 设置名称
        public static GameObject AsName(this GameObject gameObject, string name)
        {
            gameObject.name = name;
            return gameObject;
        }

        // 设置父对象
        public static GameObject AsChildOf(this GameObject gameObject, GameObject parent)
        {
            gameObject.transform.SetParent(parent.transform);
            return gameObject;
        }

        // 设置父对象
        public static GameObject AsChildOf(this GameObject gameObject, Transform parent)
        {
            gameObject.transform.SetParent(parent);
            return gameObject;
        }

        // 设置位置
        public static GameObject AsPosition(this GameObject gameObject, Vector3 position)
        {
            gameObject.transform.position = position;
            return gameObject;
        }

        // 设置位置
        public static GameObject AsRectPosition(this GameObject gameObject, Vector3 position)
        {
            if (gameObject == null)
            {
                return gameObject;
            }

            RectTransform transform = gameObject.GetComponent<RectTransform>();
            transform.localPosition = position;
            return gameObject;
        }

        // 设置旋转角度
        public static GameObject AsRotation(this GameObject gameObject, Quaternion rotation)
        {
            gameObject.transform.rotation = rotation;
            return gameObject;
        }

        // 设置缩放比例
        public static GameObject AsScale(this GameObject gameObject, Vector3 scale)
        {
            gameObject.transform.localScale = scale;
            return gameObject;
        }

        // 增加组件
        public static T GetOrAddComponent<T>(this GameObject gameObject) where T : Component
        {
            T component = gameObject.GetComponent<T>();
            if (component == null)
            {
                component = gameObject.AddComponent<T>();
            }

            return component;
        }

        // 查找子对象
        public static GameObject FindChild(this GameObject gameObject, string name)
        {
            Transform target = gameObject.transform.Find(name);
            return (target == null) ? null : target.gameObject;
        }

        public static T GetOrAddedComponent<T>(this Component component) where T : Component
        {
            if (component is null)
            {
                return default;
            }

            return component.gameObject.GetOrAddComponent<T>();
        }

        public static T GetOrAddedComponent<T>(this GameObject gameObject) where T : Component
        {
            if (gameObject is null)
            {
                return default;
            }

            T component = gameObject.GetComponent<T>();
            if (component is null)
            {
                component = gameObject.AddComponent<T>();
            }

            return component;
        }

        // 查找子对象组件
        public static T FindChildComponent<T>(this GameObject gameObject, string name) where T : Component
        {
            GameObject child = gameObject.FindChild(name);
            if (child != null)
                return child.GetComponent<T>();
            else
                return null;
        }

        // 查找父对象
        public static GameObject FindParent(this GameObject gameObject, string name)
        {
            Transform target = gameObject.transform.parent;
            while (target != null)
            {
                if (target.name == name)
                    return target.gameObject;
                else
                    target = target.parent;
            }

            return null;
        }

        // 查找父对象组件
        public static T FindParentComponent<T>(this GameObject gameObject, string name) where T : Component
        {
            GameObject parent = gameObject.FindParent(name);
            if (parent != null)
                return parent.GetComponent<T>();
            else
                return null;
        }

        // 激活对象
        public static GameObject Activate(this GameObject gameObject)
        {
            gameObject.SetActive(true);
            return gameObject;
        }

        // 禁用对象
        public static GameObject Deactivate(this GameObject gameObject)
        {
            gameObject.SetActive(false);
            return gameObject;
        }

        public static GameObject SetParent(this GameObject gameObject, GameObject parent, Vector3 position, Vector3 rotation, Vector3 scale)
        {
            if (gameObject == null)
            {
                return default;
            }

            if (parent != null)
            {
                gameObject.transform.SetParent(parent.transform);
            }

            gameObject.transform.localPosition = position;
            gameObject.transform.localRotation = Quaternion.Euler(rotation);
            gameObject.transform.localScale = scale;
            return gameObject;
        }


        public static void ResetMaterialShader(this GameObject gameObject, string fail = "Unlit/Texture")
        {
            Renderer[] renderer = gameObject.GetComponentsInChildren<Renderer>();
            if (renderer == null)
            {
                return;
            }

            for (int i = 0; i < renderer.Length; i++)
            {
                Shader shader = Shader.Find(renderer[i].sharedMaterial.shader.name);
                if (shader == null)
                {
                    shader = Shader.Find(fail);
                }

                renderer[i].sharedMaterial.shader = shader;
            }
        }

        public static SkinnedMeshRenderer Combine(this GameObject gameObject)
        {
            SkinnedMeshCombiner combiner = new SkinnedMeshCombiner();
            return combiner.GetCombinedMeshSkinnedMeshRenderer(gameObject);
        }

        public static MeshCollider GenericMeshCollider(this GameObject gameObject)
        {
            if (gameObject == null)
            {
                return default;
            }

            SkinnedMeshRenderer skinned = gameObject.GetComponentInChildren<SkinnedMeshRenderer>();
            MeshCollider collider = null;
            if (skinned == null)
            {
                Renderer renderer = gameObject.GetComponentInChildren<MeshRenderer>();
                collider = renderer.gameObject.AddComponent<MeshCollider>();
                collider.sharedMesh = renderer.GetComponentInChildren<MeshFilter>().sharedMesh;
                gameObject.SetActive(true);
            }
            else
            {
                collider = skinned.gameObject.AddComponent<MeshCollider>();
                collider.sharedMesh = skinned.sharedMesh;
            }

            return collider;
        }

        public static void DestroyMeshCollider(this GameObject gameObject)
        {
            if (gameObject == null)
            {
                return;
            }

            MeshCollider[] colliders = gameObject.GetComponentsInChildren<MeshCollider>();
            for (int i = 0; i < colliders.Length; i++)
            {
                GameObject.DestroyImmediate(colliders[i]);
            }
        }

        public static void ToCameraCenter(this GameObject gameObject)
        {
            Camera.main.ToViewCenter(gameObject);
        }


        public static void ToViewCenter(this Camera camera, GameObject gameObject)
        {
            if (camera == null || gameObject == null)
            {
                return;
            }

            var bound = gameObject.GetBoundingBox();
            var center = FocusCameraOnGameObject(camera, gameObject);
            camera.transform.localPosition = new Vector3(bound.center.x, bound.center.y, center.z);
            camera.transform.LookAt(bound.center, Vector3.up);
            camera.fieldOfView = 2.0f * Mathf.Atan(Mathf.Max(bound.size.y, bound.size.x) * 0.5f / Vector3.Distance(camera.transform.position, bound.center)) * Mathf.Rad2Deg;
        }

        public static void DrawTexture(this RenderTexture renderTexture, Rect rect, Texture texture, Material material)
        {
            RenderTexture.active = renderTexture;
            GL.PushMatrix();
            GL.LoadPixelMatrix(0, renderTexture.width, renderTexture.height, 0);
            if (material != null)
            {
                Graphics.DrawTexture(rect, texture, material);
            }
            else
            {
                Graphics.DrawTexture(rect, texture);
            }

            GL.PopMatrix();
            RenderTexture.active = null;
        }

        public static void Clear(this RenderTexture renderTexture)
        {
            RenderTexture.active = renderTexture;
            GL.PushMatrix();
            GL.LoadPixelMatrix(0, renderTexture.width, renderTexture.height, 0);
            GL.Clear(false, true, Color.clear);
            GL.PopMatrix();
            RenderTexture.active = null;
        }

        /// <summary>
        /// 获取物体包围盒
        /// </summary>
        /// <param name="obj">父物体</param>
        /// <returns>物体包围盒</returns>
        public static Bounds GetBoundingBox(this GameObject obj)
        {
            var bounds = new Bounds();
            if (obj != null)
            {
                var renders = obj.GetComponentsInChildren<Renderer>();
                if (renders != null)
                {
                    var boundscenter = Vector3.zero;
                    foreach (var item in renders)
                    {
                        boundscenter += item.bounds.center;
                    }

                    if (renders.Length > 0)
                        boundscenter /= renders.Length;
                    bounds = new Bounds(boundscenter, Vector3.zero);
                    foreach (var item in renders)
                    {
                        bounds.Encapsulate(item.bounds);
                    }
                }
            }

            return bounds;
        }

        public static Vector3 Calculate_CenterPoint(GameObject gameObject)
        {
            Renderer[] renders = gameObject.GetComponentsInChildren<Renderer>();
            if (renders == null || renders.Length <= 0)
            {
                return Vector3.zero;
            }

            if (renders.Length < 2)
            {
                return renders[0].bounds.center;
            }

            Vector3 offset = Vector3.zero;
            Vector3 top = Vector3.zero;
            Vector3 down = Vector3.zero;

            foreach (Renderer p in renders)
            {
                top = Vector3.Max(top, p.bounds.center + p.bounds.extents);
                down = Vector3.Min(down, p.bounds.center - p.bounds.extents);
            }

            offset = (top + down) / 2;
            return offset;
        }

        public static Texture2D Screenshot(this Camera camera, int width, int height, GameObject gameObject)
        {
            Vector3 position = camera.transform.position;
            Quaternion rotation = camera.transform.rotation;
            Vector3 scale = camera.transform.localScale;
            float view = camera.fieldOfView;

            camera.ToViewCenter(gameObject);
            RenderTexture renderTexture = new RenderTexture(width, height, 0, RenderTextureFormat.Default);
            camera.targetTexture = renderTexture;
            RenderTexture.active = camera.targetTexture;
            camera.Render();
            Texture2D texture = new Texture2D(width, height, TextureFormat.RGBA32, false);
            texture.wrapMode = TextureWrapMode.Clamp;
            texture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
            texture.name = gameObject.name.Replace("(Clone)", "");
            texture.Apply();
            RenderTexture.active = null;
            camera.targetTexture = null;
            camera.transform.position = position;
            camera.transform.rotation = rotation;
            camera.transform.localScale = scale;
            camera.fieldOfView = view;
            return texture;
        }

        static Bounds CalculateBounds(GameObject go)
        {
            Bounds b = new Bounds(go.transform.position, Vector3.zero);
            UnityEngine.Object[] rList = go.GetComponentsInChildren(typeof(Renderer));
            foreach (Renderer r in rList)
            {
                b.Encapsulate(r.bounds);
            }

            return b;
        }

        public static Vector3 FocusCameraOnGameObject(Camera c, GameObject go)
        {
            Bounds b = GetBoundingBox(go);
            Vector3 max = b.size;
            // Get the radius of a sphere circumscribing the bounds
            float radius = max.magnitude / 2f;
            // Get the horizontal FOV, since it may be the limiting of the two FOVs to properly encapsulate the objects
            float horizontalFOV = 2f * Mathf.Atan(Mathf.Tan(c.fieldOfView * Mathf.Deg2Rad / 2f) * c.aspect) * Mathf.Rad2Deg;
            // Use the smaller FOV as it limits what would get cut off by the frustum        
            float fov = Mathf.Min(c.fieldOfView, horizontalFOV);
            float dist = radius / (Mathf.Sin(fov * Mathf.Deg2Rad / 2f)) + 0.4f;
            c.transform.localPosition = new Vector3(c.transform.localPosition.x, c.transform.localPosition.y, -dist);
            if (c.orthographic)
                c.orthographicSize = radius;

            var pos = new Vector3(c.transform.localPosition.x, c.transform.localPosition.y, dist);
            return pos;
        }

        public static Texture2D ReadTexture2D(this RenderTexture texture)
        {
            RenderTexture.active = texture;
            Texture2D prev = new Texture2D(texture.width, texture.height, TextureFormat.ARGB32, false);
            prev.name = texture.name + "_" + Guid.NewGuid().ToString();
            prev.ReadPixels(new Rect(0, 0, texture.width, texture.height), 0, 0);
            prev.Apply();
            RenderTexture.active = null;
            return prev;
        }

        /// <summary>
        /// 设置图片
        /// </summary>
        /// <param name="view"></param>
        /// <param name="name"></param>
        /// <param name="sprite"></param>
        /// <returns></returns>
        public static GameObject SetSprite(this GameObject gameObject, string sprite)
        {
            if (gameObject == null)
            {
                return gameObject;
            }

            Sprite context = Client.Resource.LoadAsset<Sprite>(sprite, gameObject);
            if (context == null)
            {
                return gameObject;
            }

            Image img = gameObject.GetComponent<Image>();
            if (img == null)
            {
                return gameObject;
            }

            img.sprite = context;
            return gameObject;
        }

        /// <summary>
        /// 设置图片
        /// </summary>
        /// <param name="view"></param>
        /// <param name="name"></param>
        /// <param name="texture"></param>
        /// <returns></returns>
        public static GameObject SetTexture2D(this GameObject gameObject, string texture)
        {
            if (gameObject == null)
            {
                return gameObject;
            }

            Texture2D context = Client.Resource.LoadAsset<Texture2D>(texture, gameObject);
            if (context == null)
            {
                return gameObject;
            }

            RawImage img = gameObject.GetComponent<RawImage>();
            if (img == null)
            {
                return gameObject;
            }

            img.texture = context;
            return gameObject;
        }

        /// <summary>
        /// 设置图片
        /// </summary>
        /// <param name="view"></param>
        /// <param name="name"></param>
        /// <param name="sprite"></param>
        /// <returns></returns>
        public static GameObject SetSpriteAsync(this GameObject gameObject, string sprite)
        {
            if (gameObject == null)
            {
                return gameObject;
            }

            Image img = gameObject.GetComponent<Image>();
            if (img == null)
            {
                return gameObject;
            }

            void LoadSpriteCompleted(Runnable<Sprite> executed)
            {
                if (executed == null || !executed.EnsureExecutedSuccessful())
                {
                    return;
                }

                img.sprite = executed.result;
            }

            Client.Resource.LoadAssetAsync<Sprite>(sprite, gameObject).Execute(LoadSpriteCompleted);
            return gameObject;
        }

        /// <summary>
        /// 设置图片
        /// </summary>
        /// <param name="view"></param>
        /// <param name="name"></param>
        /// <param name="texture"></param>
        /// <returns></returns>
        public static GameObject SetTexture2DAsync(this GameObject gameObject, string texture)
        {
            if (gameObject == null)
            {
                return gameObject;
            }

            RawImage img = gameObject.GetComponent<RawImage>();
            if (img == null)
            {
                return gameObject;
            }

            void LoadSpriteCompleted(Runnable<Texture2D> executed)
            {
                if (executed == null || !executed.EnsureExecutedSuccessful())
                {
                    return;
                }

                img.texture = executed.result;
            }

            Client.Resource.LoadAssetAsync<Texture2D>(texture, gameObject).Execute(LoadSpriteCompleted);
            return gameObject;
        }

        public static GameObject SetText(this GameObject gameObject, object value)
        {
            if (gameObject == null)
            {
                return gameObject;
            }

            TMPro.TMP_Text text = gameObject.GetComponent<TMPro.TMP_Text>();
            if (text == null)
            {
                Text text1 = gameObject.GetComponent<Text>();
                if (text1 == null)
                {
                    return gameObject;
                }

                text1.text = value.ToString();
                return gameObject;
            }

            text.text = value.ToString();
            return gameObject;
        }
    }
}